/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package generator

import java.io.PrintWriter

import parser.Nodes._
import parser.DataTypes._
import parser.Results._
import parser.TreeWalker

/**
 * Statement printer.
 */
object StmtPrinter {

  import Generator.long2bool
  import Generator.mkEncVarDecl
  import Generator.localVarName

  /**
   * Prints "stmts"
   */
  def print(pw: PrintWriter, 
            typeM: TypeMapper,
            exprC: ExprConverter,
            enclosingVars: Set[Int],
            stmts: List[Stmt]): Unit = {
    
    TreeWalker.Walker(classOf[Stmt]).setFollower {
      f => {
        case Scope(s) => 
          pw.println("{")
          try     f(s)
          finally pw.println("}")
        case s: IfStmt =>
          pw.print("if (%s) ".format(long2bool(exprC.convert(s.cond))))
          f(s.thenStmt)
          s.elseStmt.foreach { x =>
            pw.print(" else ")
            f(x)
          }
          Success(s)

        case s: LoopStmt =>
          pw.println("for (;%s;%s)".format(
            long2bool(exprC.convert(s.cond)),
            s.update.map(exprC.convert).getOrElse("")))
          f(s.stmt)

          Success(s)

        case s: AnalyzedWhenStmt =>
          printAnalyzedWhenStmt(pw, typeM, exprC, enclosingVars, f, s)
          Success(Unit)
      }

    }.setPostprocess {
      case s: ExprStmt =>
        def short2if(e: Expr): String = {
          e match {
            case AndOp(left, right) => "if ((%s) != 0l) {\n%s;\n}\n".format(exprC.convert(left), short2if(right)) 
            case OrOp(left, right)  => "if ((%s) == 0l) {\n%s;\n}\n".format(exprC.convert(left), short2if(right))
            case e                  => exprC.convert(e)
          }
        }
        pw.println("%s;".format(short2if(s.expr)))

        Success(Unit)

      case AnalyzedEmitStmt(table, keys, value) =>
        val tableType: TableType = table.dataType.asInstanceOf[TableType]

        pw.println("emitter.emit(%s, %s, %s);".format(
          exprC.convert(table),
          keys match {
            case _:UndefLit => "Helpers.EMPTY_BYTESTRING"
            case _ => exprC.convert(keys)
          },
          exprC.convert(value)
        ))

        Success(Unit)

      case s: VarInit =>
        val t = typeM.map(s.value.dataType)
        val i = exprC.convert(s.value)
        if (enclosingVars.contains(s.id)) {
          pw.println(mkEncVarDecl(t, localVarName(s), i))
        } else {
          pw.println("%s %s = %s;".format(t, localVarName(s), i))
        }
        Success(Unit)

      case s: BreakStmt =>
        pw.println("break;")
        Success(Unit)

      case s: ContinueStmt =>
        pw.println("continue;")
        Success(Unit)

      case s: EmptyStmt =>
        pw.println(";")
        Success(Unit)

      case s: ReturnStmt =>
        pw.println("return %s;".format(exprC.convert(s.value)))
        Success(Unit)

      case s: DefVarStmt   => error("Unexpect Stmt")
      case s: DefTableStmt => error("Unexpect Stmt")
      case s: WhenStmt     => error("Unexpect Stmt")
    }.walk(stmts)
  }

  /**
   * Prints AnalyzedWhenStmt.
   */
  def printAnalyzedWhenStmt(pw: PrintWriter, 
                            typeM: TypeMapper,
                            exprC: ExprConverter,
                            enclosingVars: Set[Int],
                            f: Any => Result[Any],
                            s: AnalyzedWhenStmt) = {
    val AnalyzedWhenStmt(vars, cond, stmt) = s

    def itVarName(v: WhenVar) = { "iterator_%d_%s".format(v.id, v.name) }
    def doneVarName(v: WhenVar) = { "done_%d_%s".format(v.id, v.name) }

    vars.foreach { case v@WhenVar(_, _, container, whenType) =>
    val (indexType, itInit) = stripRef(container.dataType) match {
      case _: ArrayType  => (typeM.map(IntType), "new Helpers.IndexIterator((%s).size())".format(exprC.convert(container)))
      case MapType(k, v) => (typeM.map(k),       "(%s).keySet().iterator()".format(exprC.convert(container)))
      case _ => error("Invalid type")
    }

    pw.println("Iterator<%s> %s = %s;".format(indexType, itVarName(v), itInit))
    if (enclosingVars.contains(v.id)) {
      pw.println(mkEncVarDecl(indexType, localVarName(v), "null"))
    } else {
      pw.println( "%s %s = null;".format(indexType, localVarName(v)))
    }

    pw.println(
      "while (%s.hasNext()) {\n".format(itVarName(v)) +
      (if (enclosingVars.contains(v.id)) "\t%s.assign(%s.next());"
        else "\t%s = %s.next();").format(localVarName(v), itVarName(v)))

      if (!v.isEach) {
        pw.println("boolean %s = false;".format(doneVarName(v)))
      }
    }

    pw.print("if (%s) {\n".format(long2bool(exprC.convert(cond))))

    // Is last element?
    vars.collect { case v if (v.isAll) =>
      "!(%s).hasNext()".format(itVarName(v))
    } match {
      case Nil => pw.println("{\n")
      case x   => pw.println("if (%s) {\n".format(x.mkString(" && ")))
    }
    f(stmt)

    pw.println("}\n")

    vars.collect { case v if (!v.isEach) =>
      pw.println("%s = true;".format(doneVarName(v)))
    }
    pw.println("}")

    vars.reverseIterator.foreach { v =>
      v.whenType match {
        case _:SomeMod => pw.println("if (%s) break;".format(doneVarName(v)))
        case _:AllMod => pw.println("if (!%s) break;".format(doneVarName(v)))
        case _ =>
      }
      pw.println("}")
    }
  }
}
